#include "main.h"
#include "table.h"
#include "../lib/rtl.h"
#include "../lib/heap.h"
#include "../lib/fifo.h"
#include "../dev/keybd.h"
#include "../asm/res.h"
#include "../asm/int.h"
#include "../fs/file.h"

STATIC CHAR s[100];//测试使用

STATIC VOID Init8259A(VOID){
    __outbyte(0x20, 0x11);//magic 0x10
    __outbyte(0x21, 0x20);
    __outbyte(0x21, 0x04);//0000 0100 pin 2
    __outbyte(0x21, 0x01);
    __outbyte(0xA0, 0x11);//magic 0x10
    __outbyte(0xA1, 0x28);
    __outbyte(0xA1, 0x02);//pin 2
    __outbyte(0xA1, 0x01);
    __outbyte(0x21, 0xF8);//mask 11111000
    __outbyte(0xA1, 0xEF);//mask 11101111
}

STATIC VOID Init8253(VOID){
    __outbyte(0x43, 0x36);//magic 0x30
    __outword(0x40, 11932);//1.1932MHz => 100Hz
}

LPCSTR lpszIntDef[] = {
    "#DE Divide Error",
    "#DB Debug Exception",
    "NMI Interrupt",
    "#BP Breakpoint Exception",
    "#OF Overflow Exception",
    "#BR BOUND Range Exceeded Exception",
    "#UD Invalid Opcode Exception",
    "#NM Device Not Available Exception",
    "#DF Double Fault Exception",
    "Coprocessor Segment Overrun",
    "#TS Invalid TSS Exception",
    "#NP Segment Not Present",
    "#SS Stack Fault Exception",
    "#GP General Protection Exception",
    "#PF Page-Fault Exception",
    "",
    "#MF x87 FPU Floating-Point Error",
    "#AC Alignment Check Exception",
    "#MC Machine-Check Exception",
    "#XF SIMD Floating-Point Exception",
};

STATIC VOID IntGeneralHandler(BYTE bVector){
    REG LPCPU_CONTEXT lpCPU;
    if(bVector <= (sizeof(lpszIntDef) / sizeof(*lpszIntDef))){
        __asm__("lea 12(%%ebp),%0":"=r"(lpCPU));
        if(bVector == 0x0E)
            __asm__("mov %%cr2,%%eax":"=a"(lpCPU->err));
        RtlSPrintf(s, "%s %x", lpszIntDef[bVector], lpCPU->err);
        DrawString(s, 0, 0, FALSE);
    }
}

HANDLE hThread[100];
DWORD curThread = 0;
DWORD numThread = 1;

STATIC VOID ThTest(VOID){
    while(TRUE)
        for(CHAR c = 'a'; c <= 'z'; c++)
            DrawChar(c, 300, 100, FALSE);
}

STATIC VOID IntTimerHandler(BYTE bVector){
    (VOID)bVector;
    REG LPCPU_CONTEXT lpCPU;
    __asm__("lea 12(%%ebp),%0":"=r"(lpCPU));
    RtlCopyMemory((LPCPU_CONTEXT)hThread[curThread], (LPCVOID)lpCPU, sizeof(CPU_CONTEXT));
    curThread++;
    if(curThread >= numThread)
        curThread = 0;
    RtlCopyMemory(lpCPU, hThread[curThread], sizeof(CPU_CONTEXT));
    __outbyte(0x20, 0x60);
}

STATIC VOID CreateThread(LPVOID lpvStart, LPVOID lpvStack){
    REG LPCPU_CONTEXT lpCPU;
    lpCPU = HeapAlloc(sizeof(CPU_CONTEXT));
    RtlZeroMemory(lpCPU, sizeof(CPU_CONTEXT));
    lpCPU->cs = 0x0008;
    lpCPU->ds = 0x0010;
    lpCPU->esp = (DWORD_PTR)lpvStack;
    lpCPU->eip = (DWORD_PTR)lpvStart;
    lpCPU->eflags = 0x00000202;
    hThread[numThread++] = lpCPU;
}

CONST CHAR KeyTable[0x100] = 
    "\0\0001234567890-="
    "\b\000qwertyuiop[]"
    "\n\000asdfghjkl;\'\0"
    "\000\\zxcvbnm,./\000*"
    "\000 \0\0\0\0\0\0\0\0\0\0\0\0"
    "\000789-456+1230.";

STATIC VOID IntKeyboardHandler(BYTE bVector){
    (VOID)bVector;
    REG BYTE bKey;
    __inbyte(0x60, bKey);
    if(KeyTable[bKey])
        EgaDrawBitmap(ResASCIICharset[KeyTable[bKey] - 0x20], 160, 100);
    __outbyte(0x20, 0x61);
}

SHORT CursorX = 160, CursorY = 100;

VOID DrawCursor(VOID){
    STATIC BYTE bColors[11][6];
    STATIC SHORT PreX = -1, PreY = 0;
    if(PreX != -1)
        for(REG WORD i = 0; i < 11; i++)
            RtlCopyMemory(((LPBYTE)0xC00A0000 + PreX + (PreY + i) * 0x140), (LPCVOID)bColors[i], sizeof(bColors[i]));
    PreX = CursorX;
    PreY = CursorY;
    for(REG WORD i = 0; i < 11; i++)
        RtlCopyMemory(bColors[i], (LPCVOID)((LPBYTE)0xC00A0000 + CursorX + (CursorY + i) * 0x140), sizeof(bColors[i]));
    EgaDrawBitmapAlpha(&ResCursorBmp, CursorX, CursorY, 0x18);
}

STATUS DrawChar(CHAR c, WORD wX, WORD wY, BOOL bTransparent){
    if(c < 0x20 || c > 0x7E)
        return STATUS_INVAL;
    if(bTransparent)
        return EgaDrawBitmapAlpha(ResASCIICharset[c - 0x20], wX, wY, 0xFF);
    else
        return EgaDrawBitmap(ResASCIICharset[c - 0x20], wX, wY);
}

STATUS DrawString(LPCSTR szText, WORD wX, WORD wY, BOOL bTransparent){
    for(REG WORD i = 0; szText[i]; i++){
        DrawChar(szText[i], wX, wY, bTransparent);
        wX += GetCharWidth(szText[i]);
    }
    return STATUS_NOERR;
}

LONG GetCharHeight(CHAR c){
    if(c < 0x20 || c > 0x7E)
        return 0;
    return ResASCIICharset[c - 0x20]->Info.biHeight;
}

LONG GetCharWidth(CHAR c){
    if(c < 0x20 || c > 0x7E)
        return 0;
    return ResASCIICharset[c - 0x20]->Info.biWidth;
}

STATIC VOID IntMouseHandler(BYTE bVector){
    (VOID)bVector;
    STATIC BYTE bState = 0;
    STATIC BYTE bType;
    STATIC INT DeltaX, DeltaY;
    REG BYTE bData;
    __inbyte(0x60, bData);
    switch(bState){
    case 0:
        if(bData == 0xFA)
            bState = 1;
        break;
    case 1:
        if((bData & 0xC8) == 0x08){
            bType = bData;
            bState = 2;
        }
        break;
    case 2:
        DeltaX = bData;
        bState = 3;
        break;
    case 3:
        DeltaY = bData;
        bState = 1;
        if(bType & 0x10)
            DeltaX |= 0xFFFFFF00;
        if(bType & 0x20)
            DeltaY |= 0xFFFFFF00;
        bType &= 0x07;
        DeltaY = -DeltaY;
        CursorX += DeltaX >> 1;
        CursorY += DeltaY >> 1;
        CursorX = Range(CursorX, 0, 319);
        CursorY = Range(CursorY, 0, 199);
        DrawCursor();
        break;
    }
    __outbyte(0xA0, 0x64);
    __outbyte(0x20, 0x62);
}

STATIC VOID Render(VOID){
    {
        VECTOR2I pvPoints[] = {
            {10, 60},
            {40, 70},
            {35, 120}
        };
        EgaDrawTriangle(pvPoints, 0x40);
    }
    {
        VECTOR2I pvPoints[] = {
            {39, 70},
            {34, 120},
            {65, 130}
        };
        EgaDrawTriangle(pvPoints, 0x40);
    }
    {
        VECTOR2I pvPoints[] = {
            {38, 70},
            {64, 130},
            {95, 40}
        };
        EgaDrawTriangle(pvPoints, 0x69);
    }
    {
        VECTOR2I pvPoints[] = {
            {120, 100},
            {63, 131},
            {94, 39}
        };
        EgaDrawTriangle(pvPoints, 0x69);
    }
    {
        VECTOR2I pvPoints[] = {
            {40, 70},
            {10, 60},
            {95, 40}
        };
        EgaDrawTriangle(pvPoints, 0x92);
    }
    {
        VECTOR2I pvPoints[] = {
            {75, 30},
            {10, 60},
            {95, 41}
        };
        EgaDrawTriangle(pvPoints, 0x92);
    }
}

INT _main(VOID){
    RtlFillMemory(IDTTable, (DWORD_PTR)IntGeneralHandler, INTR_ENTRY_CNT * sizeof(LPVOID));
    IDTTable[0x20] = IntTimerHandler;
    IDTTable[0x21] = IntKeyboardHandler;
    IDTTable[0x2C] = IntMouseHandler;
    FsInitialize();
    HeapInit((LPVOID)0xC0800000, 0x800000);
    hThread[0] = HeapAlloc(sizeof(CPU_CONTEXT));
    InitIdt();
    Init8259A();
    Init8253();
    for(REG DWORD i = 0; i < 0x100; i++){
        RGBQUAD rgb;
        rgb.rgbRed = (i & 0xE0) >> 2;
        rgb.rgbGreen = (i & 0x18) << 1;
        rgb.rgbBlue = (i & 0x07) << 3;
        EgaSetPalettes(i, 1, &rgb);
    }
    EgaDrawBitmap(&ResDesktopBmp, 0, 0);
    __sti();
    InitKeyboard();
    InitMouse();
    Render();
    DrawString("Hello, World!", 100, 120, FALSE);
    DrawString("320x200 EGA", 100, 136, FALSE);
    DWORD dwRoot, dwFile;
    dwRoot = FsGetRootNode();
    dwFile = FsCreateFile(dwRoot, "sys");
    if(!dwFile)
        dwFile = FsOpenFile(dwRoot, "sys");
    FsGetFileAttributes(dwFile, &dwRoot, NULL, NULL, NULL);
    FsWriteFile(dwFile, dwRoot, (LPCVOID)"demo", 4, NULL);
    FsReadFile(dwFile, 0, s, 99, &dwRoot);
    s[dwRoot] = '\0';
    DrawString(s, 100, 152, FALSE);
    FsGetFileName(dwFile, s);
    DrawString(s, 100, 168, FALSE);
    FLOAT32 a = 3.1f;
    RtlSPrintf(s, "%x", a * a);
    DrawString(s, 100, 184, FALSE);
    {
        RECT Rect = {
            .left = 130,
            .right = 200,
            .top = 80,
            .bottom = 120
        }, Border = {
            .left = 5,
            .right = 5,
            .top = 3,
            .bottom = 3
        }, Client = {
            .left = 135,
            .right = 195,
            .top = 83,
            .bottom = 117
        };
        EgaDrawBorder(&Rect, &Border, 0xFF);
        EgaDrawRect(&Client, 0xF0);
    }
    DrawCursor();
    CreateThread(ThTest, (LPVOID)0xC0300000);
    while(TRUE)
        //__hlt();
    while(TRUE)
        for(CHAR c = 'A'; c <= 'Z'; c++)
            DrawChar(c, 280, 100, FALSE);
}

VOID Panic(LPCSTR lpPanicMsg){
    (VOID)lpPanicMsg;
    RtlFillMemory((LPVOID)0xC00A0000, 0xE0, 0xFA00);
    __cli();
    __hlt();
}